﻿using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NeuronDotNet.Core;
using NeuronDotNet.Core.Backpropagation;
using PocketEngine.Images;
namespace PocketEngine.NeuralNetworks
{
    public class pTrainingSample
    {
        public TrainingSample Sample = null;
        public virtual void Create()
        {
        }
    }

    public class pTS_pImage : pTrainingSample
    {
        public bool ShapeOnly = true;
        public pImage Image = null;
        public override void Create()
        {
            MemoryStream ts = Image.ToStream();
            int siz = 0;
            if (ShapeOnly)
            {
                siz = Image.Width * Image.Height;
            }
            else
            {
                siz = Image.Width * Image.Height * 3;
            }
            double[] vin = new double[siz];
            double[] vout = new double[1];
            int index = 0;
            while (ts.Position < ts.Length)
            {
                int r = (int)ts.ReadByte();
                int g = (int)ts.ReadByte();
                int b = (int)ts.ReadByte();
                if (Image.BytesPerPixel == 4)
                {
                    ts.ReadByte(); //skip alpha
                }
                if (ShapeOnly)
                {
                    if (((r + g + b) / 3) > 32)
                    {
                        vin[index] = 1;
                    }
                    else
                    {
                        vin[index] = 0;
                    }
                    index++;
                }
                else
                {
                    vin[index] = (float)r / 255.0f;
                    vin[index + 1] = (float)g / 255.0f;
                    vin[index + 2] = (float)b / 255.0f;
                }

            }
            vout[0] = 1;
            Sample = new TrainingSample(vin, vout);
        }
    }
    public class pTrainingData
    {
        internal TrainingSet Set;

        void AddSample(pTrainingSample sample)
        {
            Set.Add(sample.Sample);
        }
    }
    public class pNeuralNet
    {

        ActivationLayer Input;
        ActivationLayer Hidden;
        ActivationLayer Output;
        BackpropagationNetwork Network;
        public pNeuralNet(int inputs, int hiddens, int outputs)
        {
            Input = new LinearLayer(inputs);
            Hidden = new SigmoidLayer(hiddens);
            Output = new SigmoidLayer(outputs);
            new BackpropagationConnector(Input, Hidden);
            new BackpropagationConnector(Hidden, Output);
            Network = new BackpropagationNetwork(Input, Output);
            Network.Initialize();

        }
        public void Train(pTrainingData data, int count)
        {
            for (int c = 0; c < count; c++)
            {
                Network.Learn(data.Set, 1);
            }
        }
        public void Train(double[] inputs, double[] outputs, int count)
        {
            TrainingSample sample = new TrainingSample(inputs, outputs);
            TrainingSet set = new TrainingSet(inputs.Length, outputs.Length);
            set.Add(sample);
            for (int i = 0; i < count; i++)
            {
                Network.Learn(set, 1);

            }

        }
        public double Error
        {
            get
            {
                return Network.MeanSquaredError;
            }
        }
        public double[] Run(double[] inputs)
        {
            return Network.Run(inputs);
        }
    }
    public class pNh_PointInRect
    {
        internal pNeuralNet Net;
        public pNh_PointInRect()
        {
            Net = new pNeuralNet(6, 24, 1);
            Random rnd = new Random(Environment.TickCount);
            int i = 1000;
            double et = 0.001;
            Console.WriteLine("Training begins.");
            while (i > 0)
            {

                double[] ins = new double[6];
                double[] outs = new double[1];

                double x, y, w, h;
                double px, py;

                px = rnd.NextDouble();
                py = rnd.NextDouble();
                x = rnd.NextDouble();
                y = rnd.NextDouble();
                w = rnd.NextDouble();
                h = rnd.NextDouble();

                ins[0] = px;
                ins[1] = w;
                ins[2] = y;
                ins[3] = py;
                ins[4] = h;
                ins[5] = x;

                if (px >= x && px <= x + w && py >= y && py <= y + h)
                {
                    outs[0] = 1;
                }
                else
                {
                    outs[0] = 0;
                }

                Net.Train(ins, outs, 1);
                Console.WriteLine("Error:" + Net.Error);

                i--;
            }
            Console.WriteLine("Training ends.");
        }
        public void PointInRect(float px, float py, float rx, float ry, float rw, float rh)
        {
            double[] ins = new double[6];
            double[] outs = new double[1];
            ins[0] = px;
            ins[1] = rw;
            ins[2] = ry;
            ins[3] = py;
            ins[4] = rh;
            ins[5] = rx;
            outs = Net.Run(ins);
            Console.WriteLine("PIR:" + outs[0]);
        }

    }
}
